/*The basis of this project came from the 'Collision Detection' lesson of the OpenGL tutorial on  www.videotutorialsrock.com*/
#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

using namespace std;

// system includes
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <stdlib.h>
#include <set>
#include <vector>
#include <math.h>

// project includes
#include "entities/structs.h"
#include "entities/vec3f.h"
#include "utils/util.h"
#include "entities/Octree.h"
#include "movements.h"
#include "controls.h"
#include "collisions.h"
#include "textures.h"



Cell * mainCell;
vector<Cell*> _cells; //All of the cells in play
float _timeUntilUpdate = 0;

// Environment texture
GLuint environmentTx;

<<<<<<< .mine
//Environment texture translation
=======
// Cell texture
GLuint cellTx;

// Cell corona texture
GLuint coronaTx;

// Environment texture translation
>>>>>>> .r42
float envTxMove = 0;



void handleKeypress(unsigned char key, int x, int y){
	handleKeypress(key, x, y, _cells, getOctree(), mainCell);
	glutPostRedisplay();
}
void processSpecialKeys(int key, int x, int y) {
	processSpecialKeys(key,x,y,mainCell, camera);
	glutPostRedisplay();
}
void mouseButton(int button, int state, int x, int y) {
	mouseButton(button, state, x, y, mainCell);
	glutPostRedisplay();
}

void addMainCell() {
	mainCell = new Cell();
	mainCell->pos = Vec3f(0.0f, 0.0f, 0.0f);
	mainCell->v = Vec3f(0, 0, 0);
	mainCell->color = Vec3f(0, 1, 1);
	//~ mainCell->r = 0.1f * randomFloat() + 0.3f;
	_cells.push_back(mainCell);
}

void addCells(int total, float v){

	for(int i = 0; i < total; i++) {
		Cell* cell = new Cell();

		cell->pos = Vec3f(0, 0, -3.3);
		cell->pos = Vec3f(-(BOX_SIZE*randomFloat() -BOX_SIZE/2),  -(BOX_SIZE*randomFloat()-BOX_SIZE/2), -(BOX_SIZE* randomFloat()-BOX_SIZE/2));

		if(i%3==0){
			cell->v = Vec3f(- (v*randomFloat()-v), -(v * randomFloat()-v), -(v*randomFloat()-v));
		}else{
			cell->v = Vec3f(v * randomFloat() -v, v * randomFloat() -v, v * randomFloat() -v);
		}

		cell->r = 0.1f * randomFloat() + 0.3f;
		//~ cell->color =Vec3f(0.6f * randomFloat() + 0.2f, 0.6f * randomFloat() + 0.2f, 0.6f * randomFloat() + 0.2f);
		_cells.push_back(cell);
		_octree->add(cell);
	}
}

void initRendering() {
	glEnable(GL_DEPTH_TEST);
	//~ glEnable(GL_LIGHTING);
	//~ glEnable(GL_LIGHT0);
	glEnable(GL_NORMALIZE);
	glEnable(GL_COLOR_MATERIAL);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glEnable( GL_BLEND );

	environmentTx = loadPngTexture("textures/night_sky.png");
  cellTx = loadPngTexture("textures/cell01.png");
  coronaTx = loadPngTexture("textures/corona.png");

	// initial camera values
	camera = new CameraAngle();
	camera->pos_x = 0.0f;
	camera->pos_y = 0.0f;
	camera->pos_z = camera_pos_z(mainCell);
	//camera->depth = 1.5 * BOX_SIZE;

}

void handleResize(int w, int h) {
	glViewport(0, 0, w, h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, (float)w / (float)h, 1.0, 200.0);
}

void drawEnvironment(){
  glEnable(GL_TEXTURE_2D);

  // Move a little environment texture and make it smaller
  glMatrixMode(GL_TEXTURE);
  envTxMove += 0.0001;
  glTranslatef(envTxMove, 0, 0);
  glScalef(1.5, 1.5, 1.5);
  glMatrixMode(GL_MODELVIEW);

	glBindTexture(GL_TEXTURE_2D, environmentTx);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

	// Repeat texture
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

	glColor4f(1.0, 1.0, 1.0, 0.6);

	GLUquadricObj *sphere = gluNewQuadric();
	gluQuadricTexture(sphere, GL_TRUE);
	gluQuadricNormals(sphere, GLU_SMOOTH);
	gluQuadricDrawStyle(sphere, GLU_FILL);
	gluQuadricOrientation(sphere, GLU_INSIDE);
	gluSphere(sphere, BOX_SIZE, 30, 30);

	// Set texture matrix to default
	glMatrixMode(GL_TEXTURE);
	glLoadIdentity();
	glMatrixMode(GL_MODELVIEW);

	glDisable(GL_TEXTURE_2D);
}

void draw_merging_cells(){
	vector<CellPair*> tobeMerged = getToBeMergedCells();
	Cell* ball1;
	for(unsigned int i = 0; i < tobeMerged.size(); i++) {
		ball1 = tobeMerged[i]->cell1;
		glPushMatrix();
		glTranslatef(ball1->pos[0], ball1->pos[1], ball1->pos[2]);
		glColor3f(ball1->color[0], ball1->color[1], ball1->color[2]);
		glutSolidSphere(ball1->r, 12, 12);
		glPopMatrix();
	}
}

void draw_box(){
	//~ glBegin(GL_QUADS);
	//~
	//~ glNormal3f(0.0f, 1.0f, 0.0f);
	//~ glTexCoord2f(0.0f, 0.0f);
	//~ glVertex3f(-BOX_SIZE / 2, -BOX_SIZE / 2, -BOX_SIZE / 2);
	//~ glTexCoord2f(1.0f, 0.0f);
	//~ glVertex3f(-BOX_SIZE / 2, -BOX_SIZE / 2, BOX_SIZE / 2);
	//~ glTexCoord2f(1.0f, 1.0f);
	//~ glVertex3f(BOX_SIZE / 2, -BOX_SIZE / 2, BOX_SIZE / 2);
	//~ glTexCoord2f(0.0f, 1.0f);
	//~ glVertex3f(BOX_SIZE / 2, -BOX_SIZE / 2, -BOX_SIZE / 2);
	//~
	//~ glNormal3f(0.0f, -1.0f, 0.0f);
	//~ glTexCoord2f(1.0f, 0.0f);
	//~ glVertex3f(-BOX_SIZE / 2, BOX_SIZE / 2, -BOX_SIZE / 2);
	//~ glTexCoord2f(1.0f, 1.0f);
	//~ glVertex3f(BOX_SIZE / 2, BOX_SIZE / 2, -BOX_SIZE / 2);
	//~ glTexCoord2f(0.0f, 1.0f);
	//~ glVertex3f(BOX_SIZE / 2, BOX_SIZE / 2, BOX_SIZE / 2);
	//~ glTexCoord2f(0.0f, 0.0f);
	//~ glVertex3f(-BOX_SIZE / 2, BOX_SIZE / 2, BOX_SIZE / 2);
	//~
	//~ glEnd();
	//~ glShadeModel(GL_SMOOTH);
}


float camera_pos_z(){
	return camera_pos_z(mainCell);
}


void drawCells() {
	for(unsigned int i = 0; i < _cells.size(); i++) {
		Cell* cell = _cells[i];
		cell->setTexture(cellTx);
		cell->setCoronaTexture(coronaTx);
		cell->render();
	}
}


void drawScene() {
	checkCellsColors(mainCell, _cells);
	//set_maincell_position( mainCell );
	handle_merging();

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();



	gluLookAt(0,0,camera_pos_z(), mainCell->pos[0], mainCell->pos[1], mainCell->pos[2], 0.0f, 1.0f,  0.0f);
	glRotatef(camera->pos_x,0.0,1.0,0.0);
	glRotatef(camera->pos_y,1.0,0.0,0.0);


	GLfloat ambientColor[] = {0.5f, 0.5f, 0.5f, 1.0f};
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);

	GLfloat lightColor[] = {0.7f, 0.7f, 0.7f, 1.0f};
	GLfloat lightPos[] = {1.0f, 0.2f, 0.0f, 0.0f};
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos);

//	draw_box();
	draw_merging_cells();
	drawEnvironment();
	drawCells();

	glutSwapBuffers();
}

//Called every TIMER_MS milliseconds
void update(int value) {
	advance(_cells, mainCell, _octree, (float)TIMER_MS / 19000.0f, _timeUntilUpdate);
	if(_cells.size() <= 2) return;
	//	if(check_end_of_game(_balls)){
	//		cout << "EOG\n";
	//		return;
	//	}

	glutPostRedisplay();
	glutTimerFunc(TIMER_MS, update, 0);
}


int main(int argc, char** argv) {
	srand((unsigned int)time(0)); //Seed the random number generator

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(700, 700);

	glutCreateWindow("Osmos 3D");

	_octree = new Octree(Vec3f(-BOX_SIZE / 2, -BOX_SIZE / 2, -BOX_SIZE / 2),Vec3f(BOX_SIZE / 2, BOX_SIZE / 2, BOX_SIZE / 2), 1);

  addMainCell();
	addCells(50, 1.5f);
	initRendering();

	glutDisplayFunc(drawScene);
	glutKeyboardFunc(handleKeypress);
	glutSpecialFunc(processSpecialKeys);

	// mouse controls
	glutMouseFunc(mouseButton);
	glutMotionFunc(mouseMove);
	glutReshapeFunc(handleResize);
	glutTimerFunc(TIMER_MS, update, 0);

	glutMainLoop();
	return 0;
}
